#pragma once

#ifndef _CORE_QUESTION_LIBRARY_H_
#define _CORE_QUESTION_LIBRARY_H_

#include "Core\QuestionAndAnswer.h"
#include "Poco\Data\Session.h"
#include "Poco\SharedPtr.h"

namespace ExamSystem
{
    using Poco::Data::Session;
    using Poco::SharedPtr;
    /**************************************************************************\
     Component:     CQuestionLibrary
     Description:
        This class provides interface for accessing question database.
    \**************************************************************************/
    class CQuestionLibrary
    {
    public:
        CQuestionLibrary(void);
        ~CQuestionLibrary(void);

    public:
        // open the database file
        bool Initialize(const std::string &databaseFile, bool create = false);
        bool IsInitialized(void) const;

        void Finalize(void);

        // return the version of database
        std::string GetDatabaseVersion(void);

        // load a question from database
        // the question must not be null, and questionID must be set
        // return true when the operation succeeds
        bool LoadQuestion(CQuestion *question);

        // load a criterion for a question
        bool LoadCriterion(CCriterion *criterion);

        // return the number of questions of given type
        int GetQuestionCount(QuestionType type);

        // return the max ID of questions
        int GetMaxQuestionID(QuestionType type);

        // return ids and/or titles of all questions of given type
        bool LoadAllQuestion(QuestionType type, std::vector<int> &questionIds);
        bool LoadAllQuestion(QuestionType type, std::vector<QuestionBase> &questions, int start = 0, int end = -1);

        // write a question to database
        // the question must not be null, and questionID must be set
        // the database entity with the questionID will be modified
        // if questionID is -1, a now entity will be added, 
        // and the ID of question will be changed accordingly
        // return true when the operation succeeds
        // this function will change the database version
        bool SaveQuestion(CQuestion *question, CCriterion *criterion);

        // delete a question from database
        bool DeleteQuestion(QuestionType questionType, int questionID);
        bool DeleteQuestion(CQuestion *question);

    protected:
        bool CreateDatabase(void);
        bool UpdateLibraryStatus(unsigned int mask = -1);
        bool LoadQuestionTitle(CQuestion *question);

    protected:
        // handlers for different types
        // loads
        bool LoadChoiceQuestion(CQuestion *question);
        bool LoadFillingQuestion(CQuestion *question);
        bool LoadTypingQuestion(CQuestion *question);
        bool LoadQNAQuestion(CQuestion *question);
        bool LoadOfficeQuestion(CQuestion *question);
        bool LoadCodingQuestion(CQuestion *question);
        bool LoadHtmlQuestion(CQuestion *question);
        bool LoadPhotoshopQuestion(CQuestion *question);

        bool LoadChoiceCriterion(CCriterion *criterion);
        bool LoadFillingCriterion(CCriterion *criterion);
        bool LoadTypingCriterion(CCriterion *criterion);
        bool LoadQNACriterion(CCriterion *criterion);
        bool LoadOfficeCriterion(CCriterion *criterion);
        bool LoadCodingCriterion(CCriterion *criterion);
        bool LoadHtmlCriterion(CCriterion *criterion);
        bool LoadPhotoshopCriterion(CCriterion *criterion);

        // saves
        bool SaveChoiceQuestion(CQuestion *question, CCriterion *criterion);
        bool SaveFillingQuestion(CQuestion *question, CCriterion *criterion);
        bool SaveTypingQuestion(CQuestion *question, CCriterion *criterion);
        bool SaveQNAQuestion(CQuestion *question, CCriterion *criterion);
        bool SaveOfficeQuestion(CQuestion *question, CCriterion *criterion);
        bool SaveCodinglQuestion(CQuestion *question, CCriterion *criterion);
        bool SaveHtmlQuestion(CQuestion *question, CCriterion *criterion);
        bool SavePhotoshopQuestion(CQuestion *question, CCriterion *criterion);

        // common
        bool LoadFiledQuestion(CQuestion *question);
        bool LoadFiledCriterion(CCriterion *criterion);
        bool LoadPropertyCriterion(CCriterion *criterion);
        bool SaveFiledQuestion(CQuestion *question, bool withCriternion = true);
        bool SaveFiledCriterion(CCriterion *criterion);
        bool SavePropertyCriternion(CCriterion *criterion);

    protected:
        std::string m_databaseFile;
        SharedPtr<Session> m_session;
        bool m_flagInitialized;

        // handlers
        typedef bool (CQuestionLibrary::*QuestionLoader)(CQuestion *);
        static QuestionLoader ms_questionLoaders[QuestionTypeCount];
        typedef bool (CQuestionLibrary::*CriterionLoader)(CCriterion *);
        static CriterionLoader ms_criterionLoaders[QuestionTypeCount];
        typedef bool (CQuestionLibrary::*QuestionSaver)(CQuestion *, CCriterion *);
        static QuestionSaver ms_questionSavers[QuestionTypeCount];

    public:     
        static const std::string blank;
        static const std::string separator;

        static void SeparateString(const std::string &str, std::vector<std::string> &segments);
        static std::string AssembleString(const std::vector<std::string> &segments);
    };
}

#endif